From 299de92ce8574016b07f49e44f0ca2b2b1e0f4dd Mon Sep 17 00:00:00 2001 From: Andre Eikmeyer Date: Mon, 27 Jul 2026 13:57:25 +0200 Subject: [PATCH] status/volume: Redundant volume pushes cause DSP crackling Dragging the Quick Settings volume slider generates a dense sequence of value notifications. After GVC rounds a requested value and scales the channel map, several consecutive notifications can result in the same effective channel volumes. The shell currently assigns the value through the GObject property and calls push_volume() unconditionally. The property setter discards the boolean returned by gvc_mixer_stream_set_volume(), so unchanged values are sent to the audio backend again. This is audible with PipeWire DSP sinks. Volume changes pass through a virtual filter sink and its processing graph before reaching the hardware sink. The redundant pushes repeatedly update that control path while the slider is moving, producing crackles. Native hardware sinks make the same shell behavior much less apparent. GNOME Settings does not show the problem because its volume slider calls gvc_mixer_stream_set_volume() directly and only pushes the volume when the effective channel map changed. The Quick Settings slider now follows the same behavior and reuses the returned change state for notification handling. Part-of: (cherry picked from commit bd5f0a10d101d2ac2110ea8e902eb9515749324c) --- js/ui/status/volume.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js index fc49cb9ef..a01cea36a 100644 --- a/js/ui/status/volume.js +++ b/js/ui/status/volume.js @@ -180,19 +180,19 @@ const StreamSlider = GObject.registerClass({ const value = this.slider.value; const volume = value * this._control.get_vol_max_norm(); const prevMuted = this._stream.is_muted; - const prevVolume = this._stream.volume; + let volumeChanged; if (volume < 1) { - this._stream.volume = 0; + volumeChanged = this._stream.set_volume(0); if (!prevMuted) this._stream.change_is_muted(true); } else { - this._stream.volume = volume; + volumeChanged = this._stream.set_volume(volume); if (prevMuted) this._stream.change_is_muted(false); } - this._stream.push_volume(); + if (volumeChanged) + this._stream.push_volume(); - const volumeChanged = this._stream.volume !== prevVolume; if (volumeChanged && !this._notifyVolumeChangeId && !this._inDrag) { this._notifyVolumeChangeId = GLib.timeout_add_once(GLib.PRIORITY_DEFAULT, 30, () => { this._notifyVolumeChange(); -- 2.39.5